#include <vfsystem_a.h>






i_node** VFS_LookInSubdirectPTR(I_Block*_blk, char* subPATH)
{
    indPtr    ptr=0;
    size_t          i;/* I want to find Inder pointer to i_node with same name*/
   for(i=0;i<I_N_LEVELS;++i)/*From direct to tripple undirect*/
   {
       if((_blk->N_directArr)[i])
       {
           ptr=VFS_FindPTR((_blk->N_directArr[i]),i,subPATH);/*Recurcivey checks name*/
           if(ptr)
            {
                    return (i_node**)ptr;/*Local*/
            }
       }
   }
   return 0; /* Folder not found*/
}

void VFS_removerofinode(i_node** _remnode,i_node* _parnt)
{
    size_t             lastpos;
    i_node**           parntPTRlast;
/*Free memory and update parent and fill empty place by last object*/

    lastpos=_parnt->i_n_blk->numofptr;
    parntPTRlast=(i_node**)VFS_Getpos(_parnt,lastpos-1);/*find last object*/
    if(*parntPTRlast==*_remnode)
    {
        MM_Free(Heap,(void*)*parntPTRlast);
        *parntPTRlast=0;
    }
    else
    {
        MM_Free(Heap,(void*)*_remnode);
        *_remnode=*parntPTRlast;
        *parntPTRlast=0;
    }
    --_parnt->i_n_blk->numofptr;
    _parnt->i_n_blk->sizeData+=-sizeof(void*);
}


/*Remove file and Folder in lincount=0*/
inode_Err VFS_remove(char* _PATH)
{
    char            PATHsourse[PATH *3];
    char            name[PATH];
    i_node**         remnode;
    i_node*         parnt;

    if(!_PATH)
    {
        return VFS_ONE_OF_ARGUMENTS_IS_WRONG_RM;
    }
    strcpy( PATHsourse,_PATH);

    /*Find item in a PATH*/
    parnt=VFS_PATHpreparing(PATHsourse,INOUT name);
    if(parnt==0)
    {
        return VFS_PARENT_DIRECTORY_NOT_FOUND;
    }
    if(parnt->mode!=VFS_DERECTORY)
    {
        printf("\nPATH NOT EXISTS\n");
        return VFS_PATH_NOT_EXISTS;
    }
    if(!(remnode=VFS_LookInSubdirectPTR(parnt->i_n_blk,name)))
    {
        printf("\nPATH NOT EXISTS\n");
        return VFS_PATH_NOT_EXISTS;
    }


    if((*remnode)->i_n_blk->Link_count>1)/*possible only with files*/
    {
        VFS_removerofinode(remnode,parnt);
          return 0;
    }

    if((*remnode)->i_n_blk->Link_count==1)
    {
        if((*remnode)->mode==VFS_DERECTORY)
        {
            /*check is it empty*/
            if((*remnode)->i_n_blk->numofptr>2)
            {
                printf("\n Folder is not empty can'n be removed\n");
                return -1;
            }
        }

        if((*remnode)->mode==VFS_FILE && (*remnode)->mode==VFS_HLINK )
         {
           if((*remnode)->i_n_blk->numofptr>0)
            {
                /*need to free avery pages inside and after that only free the i_node*/
            }
         }
         else
            {
              VFS_removerofinode(remnode,parnt);
            }
    }
    return -1;
}


/*THe full path has to be provided */
inode_Err VFS_ln (i_mode mode,char* _PATH1,char* _PATH2)
{
    char            _PATHsourse[PATH *3];
    char            _PATHdest[PATH*3];
    char             name[PATH];
    i_node*          parnt;
    i_node*          ln2parnt;
    i_node*          node;
    inode_Err        err;

    if((!_PATH1) && (! _PATH2))
    {
        return VFS_ONE_OF_ARGUMENTS_IS_WRONG_LN;
    }
        strcpy( _PATHsourse,_PATH1);
        strcpy( _PATHdest,_PATH2);

    /* i want create the Hard link*/
    if(mode==VFS_HLINK)
    {
        /*Find parent i_node* or *Check is the _PATH1 is valid*/
        parnt=VFS_PATHpreparing(_PATHsourse,INOUT name);
        if(parnt==0)
        {
            return VFS_PARENT_DIRECTORY_NOT_FOUND;
        }
        if(parnt->mode!=VFS_DERECTORY)
        {
            printf("\nPATH NOT EXISTS\n");
            return VFS_PATH_NOT_EXISTS;
        }
         if(!(node=VFS_LookInSubdirect(parnt->i_n_blk,name)))
         {
             printf("\nPATH NOT EXISTS\n");
             return VFS_PATH_NOT_EXISTS;
         }
         if(!(node->mode==VFS_FILE || node->mode==VFS_HLINK))
         {
             printf("\nWrong object to H_link\n");
             return VFS_WRONG_OBJECT;
         }
         parnt=node;


   /*Check is the _PATH2 is valid*/

        ln2parnt=VFS_PATHpreparing(_PATHdest,INOUT name);
        if(ln2parnt==0)
        {
            return VFS_PARENT_DIRECTORY_NOT_FOUND;
        }
        if(ln2parnt->mode!=VFS_DERECTORY)
        {
            printf("\nPATH NOT EXISTS\n");
            return VFS_PATH_NOT_EXISTS;
        }
        if((node=VFS_LookInSubdirect(ln2parnt->i_n_blk,name)))
         {
             printf("\nPATH NOT EXISTS\n");
             return VFS_MKDIR_DIRECTORY_ALREADY_EXIST;
         }

    /*create i_node with flag H_Likn and makes its I_BLock as _PATH1 (parnt) and update lincount*/

        node=VFS_CreateInodes(name,VFS_HLINK,ln2parnt,&err);/*Also and update parent*/
        node->i_n_blk=parnt->i_n_blk;
        ++node->i_n_blk->Link_count;
        err=VFS_Insert2Parent(ln2parnt->i_n_blk,&node);/*if now space need to free node*/
        if(err)
        {
            return err;
        }
        VFS_UpdateParentDirectory(ln2parnt);
        return 0;
    }
    return -1;
}












inode_Err read_pos(i_node* _node,size_t _position,char* _onechar)
{
    char**      data;
    int         pos1=_position/I_N_BLOCKS;
    int         pos2=_position%I_N_BLOCKS;
    /*check does the data already created and i start to overwrite it*/
    data=(char**)VFS_Getpos(_node,pos1);
   * _onechar=((char*)(*data))[pos2];

    if(pos1+1>_node->i_n_blk->numofptr)
    {
    _node->i_n_blk->numofptr=pos1+1;
    }
    return 0;
}

inode_Err VFS_read(int _fdindex,int _size, char* _buffer)
{
    inode_Err   err;

    if(_fdindex<0)
    {
        printf("\nNo file to READING was provided\n");
        return VFS_NO_FILE_TO_READING;
    }
    if(_fdindex>fdcounter)
    {
        printf("\n Illigal fdcounter was provided");
        return VFS_ILLIGA_POSITION_READING;
    }

    if(_buffer==0)
    {
        printf("\n No buffer supplyed to read in");
        return VFS_NO_BUFFER_SUPPLYED_READ;
    }

    if(sizeof(void*)*(fdlist[fdcounter-1]->pos+_size)>fdlist[fdcounter-1]->node->i_n_blk->sizeData)
    {
        printf("\n Size for reading out of boundaries");
        return VFS_SIZE_FOR_READING_OUT_OF_BOUNDARIES;
    }

    /*Start to read byte byte*/
    for(;_size && _buffer ;--_size)
    {
         if((err=read_pos(fdlist[fdcounter-1]->node,fdlist[fdcounter-1]->pos,_buffer)))
         {
             return err;
         }
         ++_buffer;
         ++fdlist[fdcounter-1]->pos;
    }
    return 0;
}






/*********************************************************************************************************/

/*Allocate the one level of some inderection levels*/
void VFS_AllocateLevelOfInderect(i_node*_node)
{
    size_t   i;
    for(i=0;_node->i_n_blk->N_directArr[i];++i);
    VFS_Createinderlevels(&(_node->i_n_blk->N_directArr[i]),i);
    return;
}

/*Function will write byte byte into datapages, each page have defoult size with bytes size
I_N_BLOCKS* (void*)   */
/* _position is the index of direct/indirect level   */
inode_Err write_pos(i_node* _node,size_t _position,void* _onechar)
{
    char**      data;
    char*       data1;
    err_MM      err;/* in every page there are severap bytes=I_N_BLOCKS*/
                                           /*check if I am i endof page*/
    int         pos1=_position/I_N_BLOCKS;
    int         pos2=_position%I_N_BLOCKS;
    /*check does the data already created and i start to overwrite it*/
    data=(char**)VFS_Getpos(_node,pos1);

    if(data==0)/*new inderection level has to be open*/
    {
        data1=MM_Malloc(Heap,I_N_PAGESIZE,&err);
        ((char*)(data1))[pos2]=*(char*)_onechar;
        VFS_Insert2Parent(_node->i_n_blk,(i_node**)&(data1));
    }
    else
    {
        if(*data==0)
        {
            *data=MM_Malloc(Heap,I_N_PAGESIZE,&err);
            ((char*)(*data))[pos2]=*(char*)_onechar;
        }
        else
        {
            ((char*)(*data))[pos2]=*(char*)_onechar;
        }

    }
    if(pos1+1>_node->i_n_blk->numofptr)
    {
    _node->i_n_blk->numofptr=pos1+1;
    _node->i_n_blk->sizeData=I_N_PAGESIZE * _node->i_n_blk->numofptr;
    }
    return 0;
}

inode_Err VFS_write(int _fdindex,int _size, void* _buffer)
{
    inode_Err   err;

    if(_fdindex<0)
    {
        printf("\nNo file to READING was provided\n");
        return VFS_NO_FILE_PROVEDED_TO_WRITE;
    }
    if(_fdindex>fdcounter)
    {
        printf("\n Illigal fdcounter was provided");
        return VFS_ILLIGAL_FDCOUNTER_WRITE;
    }

    if(!_buffer)
    {
        printf("\n Nothing to write");
        return VFS_NOTHING_TO_WRITE;
    }

    /*Start to write byte byte*/
    for(;_size && _buffer ;--_size)
    {
         if((err=write_pos(fdlist[fdcounter-1]->node,fdlist[fdcounter-1]->pos,_buffer)))
         {
             return err;
         }
         (char*)++_buffer;
         ++fdlist[fdcounter-1]->pos;
    }
    return 0;
}


int PreparetoReading(char* _filename,flag_S _flag)
#define  modeofopen(x) (x==S_READ)?    "READING":"WRITING"
{
    err_MM          err1;
    inode_Err       err;
    char           _newinodePATH[100];
    char           _PATH[100];
    int             i;
    FD*             fd;
    i_node*         parnt;
    i_node*         node;
    strcpy(_PATH,_filename);

    /*find parent i_node*/
    parnt=VFS_PATHpreparing(_PATH,INOUT _newinodePATH);
    /*check does the file it exist if not create it*/
    if(parnt==0)
    {
        return VFS_PARENT_DIRECTORY_NOT_FOUND;
    }
    if(parnt->mode!=VFS_DERECTORY)
    {
        printf("\nPATH NOT EXISTS\n");
        return VFS_PATH_NOT_EXISTS;
    }

    /*need checking is the file with same name allready exist*/
    /*if not exist chage flag_S to creat*/
    if(!(node=VFS_LookInSubdirect(parnt->i_n_blk,_newinodePATH)))
    {
        _flag=S_CREATE+S_WRITE+S_READ;
    }

    if(_flag>=S_CREATE)
    {
        /* the i_node  file has to be prodeced*/
        node=VFS_CreateInodes(_newinodePATH,VFS_FILE,parnt,&err);/*Also and update parent*/
        VFS_AllocateLevelOfInderect(node);/*Same as Directory*/
        if(node==0)
        {
            return VFS_FILE_CREATION_PROBLEMS;
        }
        err=VFS_Insert2Parent(parnt->i_n_blk,&node);/*if now space need to free node*/
        if(err)
        {
            return err;
        }
        node->i_n_blk->numofptr=0;
        node->i_n_blk->sizeData=0;
        if(err)
        {
            printf("\nProblems with ParetnDirectoryUpdating\n");
            return err;/*PROBLEMS WITH UPDATE PARENT DIRECTORY*/
        }

            printf("\n!!File was succesfully Create and open to writing and reading!!\n");
    }
    else
    /* In case that file is not open to creat ihttps://opersystem.googlecode.com/svn/trunk/ts flage has to be changed according to flag*/
    {
        /*is the file alrady in fdlist*/
        for(i=0;i<fdcounter && node!=fdlist[i]->node;++i);

        if(i<fdcounter)/*File alreadey open and now can be used as read only*/
        {
            printf("\nFile was already open, you can use it !!!!\n");
            return i-1;/*ist index*/
        }
        else/*open file, put it in fd linst get fd index*/
        {
            printf("!!File was succesfully open to %s !!",modeofopen(_flag));
        }
    }
    fd=MM_Malloc(Heap,sizeof(FD),INOUT &err1);
    if(err1)
    {
        return VFS_PROBLEMS_FD_FILE_OPENING;
    }
    fdlist[fdcounter]=fd;
    ++fdcounter;
    fd->node=node;
    fd->pos=0;
    return fdcounter -1;
}

int openfile(char* _PATH1,flag_S _flag)
{
    int         fd_index_opened_file;
    /*opening file *
      0)Finde derectory and create i_node
      1)Creat FiLe_Descriptor struct
      2)Put pointer to it in fdlist its global
      3)accept fd of open file=Get the index
      4)Put index in userdata(var_t)
      5)Change status of  is file open to open("1") for ativation read function on Next run
      6)For testing print the data in buffer to control the driver work, ad 1st time put '\0'
    {*/
        /*here the openg process*/
    fd_index_opened_file=PreparetoReading(_PATH1,_flag);
    return fd_index_opened_file ;/*returning the fd index of open file*/
}


/******************************************************************************************/
indPtr VFS_FindbyIndexPTR(indPtr _indPTR,size_t _index,ind_lvl _level)/*its the adress of indPtr*/
{
#ifdef unitest_VFS_System
    indPtr test=_indPTR;
#endif /*unitest_VFS_System*/
    indPtr temp;

    if( _indPTR==0)
    {
        return 0;
    }
    if(_level==0)/*Works on data level Datalevel*/
    {
        return &(( _indPTR)[_index]);
    }
    /*NEED TO CHOOSE within which block i need to seek*/
    /*need to return i one position back*/
    temp=VFS_FindbyIndexPTR(_indPTR[_index/I_N_BLOCKS],_index-I_N_BLOCKS*(_index/I_N_BLOCKS),_level-1);/*To down by one level*/
    if(temp==0)/*If temp==0 the empty cell will be in next block*/
    {
        return 0;/*Wrong index was prowided*/
    }
    return temp;/*adress or pointer to void* ore i_node*/
}


int VFS_GetIndexInLevel(int* _pos)
{
    int level=0;
    if(*_pos>=(int)(I_N_BLOCKS*(pow(I_N_BLOCKS,I_N_LEVELS)-1)/(I_N_BLOCKS-1)))
    {
        return -1;
    }
    size_t posinder=(size_t)pow(I_N_BLOCKS,level+1);
    while(*_pos>=posinder)
    {
        ++level;
        posinder+=(size_t)pow(I_N_BLOCKS,level+1);
    }
    posinder+=-(size_t)pow(I_N_BLOCKS,level+1);
    *_pos=*_pos-posinder;
    return level;
}


void** VFS_Getpos(i_node* _node,int _pos)
{
    /*determide the level of inderection*/
    int level=VFS_GetIndexInLevel(&_pos);
    if(-1==level)
    {
        printf("\n!!! Get_pos  illigal position provided!!!!\n");
        return 0;
    }
#ifdef unitest_VFS_System
    indPtr test=_node->i_n_blk->N_directArr[level];

#endif /*unitest_VFS_System*/
    test=VFS_FindbyIndexPTR(_node->i_n_blk->N_directArr[level],_pos,level);
    return test;
}











